詳解Terraform第3版 5章〜6章
https://gyazo.com/f7bec0609a1a743fbb4ca5e3e0e50472
5章 ループや分岐、デプロイやつまずきポイント
ビルトイン count, length
ビルトイン count パラメータ、count.index による添字抽出
ビルトイン length と variable の組み合わせ
code:iamusers.tf
resource "aws_iam_user" "example" {
count = length(var.users)
}
variable "users" {
type = list(string)
}
残念ながら count には制限がある
リソース全体のループに使用できるがインラインブロックでは使えない e.g. tag { ... }
上記の bar だけ削除したい場合にリストから削除すると、bar を boo に変更する適用となるので注意
制限事項を解消するため for_each が導入された
for_each
toset(var.list) を使うことで集合に変換する
values(aws_iam_user.example)[*].arn のようにマップから値を返す関数もある
インラインブロックにおいても利用できる e.g. dynamic "tag" {...}
コラム
タグの強制をするというのはよい習慣のひとつ
各モジュールの aws プロバイダで default_tags ブロックを設定できる
for
Pythonコードを例に類似した機能を提供する for を説明
code:foo.py
print(upper_case_names)
# filter
print(short_upper_case_names)
condition 条件分岐
ロックが外れず
https://gyazo.com/658cb9a80beae22ba2b43f1fdb16ce4d
途中 terraform force-unlock ${LockID} するなどした
auto scaling schdule を商用環境にのみ適用する例
count パラメータを使って実現する方法
count = var.enable_autoscaling ? 1 : 0 の三項演算子
count = 0 ならばリソース作成をしないということ
tkdn.icon if-else の場合は冗長になりがちだわね
for_each, for を使う条件分岐
code:example.tf
dynamic "tag" {
for_each = {
for key, value in var.custom_tags:
key => uppper(value)
if key != "Name"
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
ゼロダウンタイムデプロイ
terraform の変更の際にアトミックな変更を行おうとしてダウンタイムが発生する場合がある
トラブルシュート
何度か繰り返しているからかエラーが出た
https://gyazo.com/b54c698f800beb742d315952308a0ae5
Issue コメントにある手順をやっても解決しなかった
繰り返して書き換えているうちに ingress ブロックがあるにも関わらず、aws_security_group_rule リソースで ingress を指定しているというおかしな記述が問題だった
Plan: 1 to add, 1 to change, 1 to destroy.
例では起動設定 aws_launch_configuration が置き換えられ、既存のASGを新しい起動設定で書き換える
新しい起動設定の参照へ変わっただけではまだインスタンスは起動していない状態
作り直す手段もあるがどちらにせよダウンタイムが発生する
2章で登場した create_before_destroy ライフサイクルの出番
1. ASG name パラメータは起動設定の名前を参照するようにする
起動設定の変更のたびに名前が変わるのでTerraformはASGの置き換えを強制させられる
2. ASG create_before_destory = true としてASG 置き換え時に元の ASG を削除する前に作成する
3. ASB min_elb_capacity パラメータはクラスタの min_size に合わせる
新しい起動設定のASGが作成されるのが先でEC2インスタンスが起動して張り替えられるのはそこまで遅くないが、古いASG破棄に時間がかかった
書籍の図解がわかりやすい
Terraform 実行中に何らか問題が起きた場合は自動的に Terraform がロールバックまで行う = 新しい ASG のインスタンスは ALB に登録されない
つまずきポイント
Terraform はリソース作成・変更前において plan 中に計算できる count, for_each ではないと実行できない
ハードコードされた = 計算結果が決定的であるものだけで、ランダム値などは参照できない
ゼロダウンタイムデプロイの例に上げた create_before_destory は AutoScaling ポリシーに合わせて使えない
ゼロダウンタイムデプロイについてはフルマネージドなクラウドが提供するものを使うべき(それはそう
先ほどのサンプルから変更を加える
code:refresh.tf
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
}
}
ただし時間がめちゃめちゃかかるらしい、2台置き換えに20分くらい…
Terraform を使いだしたら Terraform だけを使うこと(import コマンドも適宜使う)
IaCの意味がまったくなくなる、なるほどな
Terraform のリソースドキュメントの下部に必ず import がある
リファクタリングはむずい
変数の変更やリソース名の変更が削除・新規作成となる
create_before_destroy 戦略が使えるか確認
Terraform State は手で更新せず terraform state mv を使う、moved ブロックを使う
code:1.1からのmovedブロック.tf
moved {
from = aws_security_group.instance
to = aws_security_group.cluster_instance
}
これで apply すると state のみ更新される
イミュータブルなパラメータも確認する
tkdn.icon ドキュメントだとどんなふうに書かれているんだ?
6章 シークレット管理
序
シークレットはプレーンテキストで保存しない
どうする
クラウドプロバイダではKMS(key management system)を提供
PGPキー(公開鍵, 秘密鍵)
集中型のシークレットストア = tkdn.icon 1password みたいなやつかな
UI, API, CLI 提供の区分けなど
ユーザごとのセキュアな方法
人のユーザ
環境変数
明確に意識しなかったが環境変数もメモリ上にしか残らないわけじゃなくて、shell history に残る
1password の例
tkdn.icon 知ってたけど使ってなかったので使ってみよう
マシンユーザ
CI/CD やパイプラインで必要となる場合の認証情報
どのマシンからどのマシンへ認証するかが重要
3パターンで解説
CircleCI(シークレットを保存)
CircleCIコンテキストに保存し環境変数に読み出す
デメリット: 手動かつ認証情報が永続的
JenkinsをCIサーバとしてEC2で動かす(IAMロールの利用)
EC2インスタンスにAssumeRoleする
1. IAMロールを引き受けられるか定義したロール引受のためのIAMポリシー作成
2. IAMロールの作成(ロールを引き受けられるのみ)
3. 実際に権限を持つためのIAMポリシー作成
4. 実際の権限をIAMロールにアタッチする
5. EC2インスタンスプロファイルを作成しインスタンスが利用できるようにする
GitHub Actions(OIDCを使用)
AWSのようなプロバイダにアクセスする際にはワークフロー中に認証できる方法が必要
1. まず必要なリソースは aws_iam_openid_connect_provider リソースの作成
AWSアカウント内に IAM OIDC ID プロバイダを作成し GitHub Actions サムプリントを信頼するよう設定する
2. IAM OIDC ID プロバイダがフェデレーション認証を通じAssumeRoleできるポリシーを作成
全リポジトリではなく任意のリポジトリ、ブランチなどの指定をする
3. 操作用のIAMロール作成し上記のポリシーを引き受ける
4. 操作用のIAMロールにEC2Admin権限をアタッチ
5. Actions で OIDC を使って AWS へ認証
苦戦しつつなんとかできた
リソースとデータソース
認証情報をプレーンテキストにいれるな!!
方法がいくつか
環境変数
export TF_VAR_db_username=(DB_USERNAME) などが典型的
だが、どこに文字列が保存されているかについては答えられないソリューション
1password, Hashicorp Vault...などの集中型シークレットストア
デメリットとしては
Terraform にすべてが定義されていない
シークレット管理方法の標準化が確立されない
Terraformコードのバージョン管理とシークレット管理が別軸になる
暗号化ファイル
ファイルを暗号化し保存しバージョン管理する( tkdn.icon 正気か?
AWS KMS keys を暗号鍵としてシークレットのまとまったyamlファイルを暗号化する寸法
data "aws_caller_idintity" "self" {} で現在のユーザ情報が取得できるのは知らなかった
コードと同じく管理される一方で、暗号化されたファイルの更新忘れ、復号したファイルが残る、バージョン管理に誤って含めるなど注意点が多い。またローテーションや無効化を考えると手間も増える
AWS KMS に保存したキー、APIコールなどを考えると金銭的コスト面もデメリット
シークレット管理方法も標準化も難しい
シークレットストア
AWS SecretManager が典型的。ここでは先にマネコンから作成して読み出す
SecretManager ではデータ保存の推奨フォーマットはJSON
data ブロックで読み出して locals ブロックでローカ変数にセットし db resource 作成時にシークレットを渡す
メリットとしては、コード管理に含めなくてよい、Web UI で保存できる、ローテーションや無効化がフルマネージド、audit log も完備、管理が標準化される
デメリットとしては、バージョン管理やコードが同期しないので設定漏れなども、コストがつく、テストが外部システムに依存することになるので自動テストは難しい
ステートファイル・プランファイル
ステートファイル
いろいろシークレット管理に書いてきたが、Terraform state ファイルにはプレーンテキストで保存される
暗号化をサポートするTerraform バックエンドに tfstate を保存する(S3など), バックエンドのアクセスを制御する
プランファイル
また -out=exmaple.plan オプションでエクスポートされるファイルもまたプレーンテキストで保存される
tkdn.icon 使うシーンがなければ使わないほうがよさそう
まとめ
シークレットはプレーンテキストで保存するな
人間はシークレットマネージャを利用して環境変数に設定しろ
マシンユーザは認証情報、IAMロール、OIDCを使え
トレードオフ表は書籍参考
tfstate, plan ファイルにもプレーンテキストでシークレットが含まれる